package com.ikas.ai.server.module.steady.delaytask;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.ikas.ai.server.module.alarm.enums.SolvedEnum;
import com.ikas.ai.server.module.steady.enums.AlarmConditionEnum;
import com.ikas.ai.server.module.steady.enums.DelayStatusEnum;
import com.ikas.ai.server.module.steady.model.po.SteadyAlarmData;
import com.ikas.ai.server.module.steady.service.SteadyAlarmDataService;
import com.ikas.ai.server.module.steady.service.SteadyMonitorMeteService;
import com.ikas.ai.utils.DateTimeUtil;
import com.ikas.ai.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.sql.Timestamp;
import java.util.Objects;
import java.util.Set;

/**
 * @author: wang.qining
 * @date: 2024/3/7 15:02
 */
@Slf4j
@Component
public class SteadyDelayService implements InitializingBean {

    public static final String ORDER_DELAY_TASK_KEY = "steady:delaytask";
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private SteadyMonitorMeteService steadyMonitorMeteService;
    @Resource
    private SteadyAlarmDataService steadyAlarmDataService;

    /**
     * 生成延时队列
     *
     * @param meteId        测点id
     * @param steadyAlarmId 稳态预警id
     * @param duration      延时时长
     */
    public void produce(Long meteId, Long steadyAlarmId, Long alarmTime, Integer duration) {
        log.info("添加阈值预警记录：" + "测点ID：" + meteId + ",预警记录id：" + steadyAlarmId + ",延迟预警时间:" + duration + ",当前时间" + DateTimeUtil.getDateTimeString());
        // 放到延时队列中的数据格式是：meteId,steadyAlarmId
        String data = meteId + "," + steadyAlarmId + "," + alarmTime;
        long time = System.currentTimeMillis() + duration * 1000;
        redisUtil.getZSetOperations().add(
                ORDER_DELAY_TASK_KEY,
                data,
                time
        );

    }

    //延时任务，也是异步任务，延时任务达到时效做延时预警逻辑处理
    public void consuming() {
        Long currentTime = System.currentTimeMillis();
        Set<ZSetOperations.TypedTuple<String>> steadySet = redisUtil.getZSetOperations().rangeByScoreWithScores(
                ORDER_DELAY_TASK_KEY,
                //延时任务score最小值
                0,
                //延时任务score最大值（当前时间）
                currentTime
        );
        if (steadySet != null && steadySet.size() > 0) {
            for (ZSetOperations.TypedTuple<String> steady : steadySet) {
                try {
                    String data = steady.getValue();
                    String[] split = data.split(",");
                    Long meteId = Long.valueOf(split[0]);
                    Long steadyAlarmId = Long.valueOf(split[1]);
                    SteadyAlarmData alarmData = steadyAlarmDataService.getById(steadyAlarmId);
                    // 查询此时该测点是否已经产生了预警，如果有，则不再产生
                    // 存在一种情况：工况结束时，会将该工况中产生的预警记录删除，所以alarmData可能为空
                    if (alarmData == null || (Objects.equals(alarmData.getSolved(), SolvedEnum.UN_SOLVED.getSolved()) && alarmData.getDelayStatus() == 2)) {
                        // 直接删除延时任务
                        redisUtil.getZSetOperations().remove(ORDER_DELAY_TASK_KEY, steady.getValue());
                        return;
                    }
                    // 根据id去查询告警产生时间(即测点上报时间)-延时时间，这一整个区间的数据，根据预警条件(大于小于)确定是返回最大值还是最小值
                    // 如果是大于，则返回最小值，逻辑是：如果该区间的最小值都大于当前值，则说明应该产生预警
                    // 如果是小于，则返回最大值，逻辑是：如果该区间的最大值都小于当前值，则说明应该产生预警
                    Double alarmThreshold = alarmData.getAlarmThreshold();
                    // 此处封装tdEngine查询开始及结束时间
                    long tdEngineSelectStartTime = Long.parseLong(split[2]);
                    long tdEngineSelectEndTime = tdEngineSelectStartTime + alarmData.getDelayAlarmDuration() * 1000;
                    Timestamp alarmTime = DateTime.of(tdEngineSelectEndTime).toTimestamp();
                    log.info("创建" + steady.getValue() + "预警记录" + "_当前时间" + DateTimeUtil.getDateTimeString() + "_预警时间" + alarmTime);
                    //预警条件
                    AlarmConditionEnum alarmCondition = AlarmConditionEnum.getEnumByValue(alarmData.getAlarmCondition());
                    log.info("alarmCondition:{}", alarmCondition);
                    Double extremeValue = steadyMonitorMeteService.getExtremeValueFromTd(meteId, tdEngineSelectStartTime, tdEngineSelectEndTime, alarmCondition, 0);

                    // 更新预警记录
                    SteadyAlarmData steadyAlarmData = new SteadyAlarmData();
                    steadyAlarmData.setId(steadyAlarmId);
                    // 算出来绝对值
                    if (steadyMonitorMeteService.isMeetCondition2(alarmThreshold, extremeValue, alarmCondition)) {
                        // 预警，更新预警时间和预警状态
                        steadyAlarmData.setAlarmTime(alarmTime);
                        steadyAlarmData.setDelayStatus(DelayStatusEnum.DELAY_ALARMED.getValue());
                    } else {
                        // 不预警
                        steadyAlarmData.setDelayStatus(DelayStatusEnum.DELAY_NOT_ALARMED.getValue());
                    }
                    steadyAlarmDataService.updateById(steadyAlarmData);

                    // 关闭之后，将延时任务从队列中删除
                    redisUtil.getZSetOperations().remove(ORDER_DELAY_TASK_KEY, steady.getValue());
                } catch (Exception e) {
                    log.info("消费队列时发生错误：" + e.getMessage());
                    redisUtil.getZSetOperations().remove(ORDER_DELAY_TASK_KEY, steady.getValue());
                }

            }
        }
    }

    public static void main(String[] args) {
        Timestamp alarmTime = DateTime.of(1713160914047L).toTimestamp();
        String s = DateTimeUtil.formatTimeStr(1713160914047L);
        System.out.println(alarmTime);
    }

    //该类对象Bean实例化之后，就开启while扫描任务
    @Override
    public void afterPropertiesSet() throws Exception {
        new Thread(() -> {
            while (true) {
                consuming();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    log.error("消费延时队列失败：" + e.getMessage());
                    Thread.currentThread().interrupt();
                }
            }
        }).start();
    }

}
